package org.ns.longhttp.selector.datamapper;

import org.ns.longhttp.BeanUtil;
import org.ns.longhttp.Server;
import org.ns.longhttp.ThreanName;
import org.ns.longhttp.config.ServerConfig;
import org.ns.longhttp.selector.datamapper.responseid.DataResponseIdFactory;
import org.ns.longhttp.selector.datamapper.responseid.DataResponseIdService;
import org.ns.longhttp.selector.datamapper.responseid.DefaultDataResponseIdFactory;
import org.ns.longhttp.wrapper.MapWrapper;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;

/**
 * 数据映射队列
 */
public class DataMappingQueue {

//    private final static DataMappingQueue instance = new DataMappingQueue();
//
//    public static DataMappingQueue getInstance() {
//        return instance;
//    }

    public DataMappingQueue () {

    }

    // 数据清理轮训线程
    private ScheduledExecutorService clearLoopThread
            = Executors.newScheduledThreadPool(1, new ThreanName("dataQueue-ClearLoopThread", true));
    // 清理每个资源的数据缓存
    private ExecutorService clearThread;

    private final MapWrapper<String, DataMappingEntry> dataEntryMap = new MapWrapper<>();
    private DataResponseIdFactory dataResponseIdFactory = DefaultDataResponseIdFactory.get();
    private int timeout = 20;
    // 数据映射实体的实现对象。如果不传则使用默认的方案
    private Class<? extends DataMappingEntry> dataMappingEntryType = DefaultDataMappingEntry.class;

    /**
     * 设置数据映射的实体处理类型
     * @param dataMappingEntryType
     */
    public void setDataMappingEntryType(Class<? extends DataMappingEntry> dataMappingEntryType) {
        this.dataMappingEntryType = dataMappingEntryType;
    }

    /**
     * 应答包生成有序包ID的工厂
     * @param dataResponseIdFactory
     */
    public void setDataResponseIdFactory(DataResponseIdFactory dataResponseIdFactory) {
        this.dataResponseIdFactory = dataResponseIdFactory;
    }

    /**
     * 数据缓存的时间
     * @param timeout
     */
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public DataMappingEntry getDataMappingEntry(String key) {
        return dataEntryMap.get(key, ()->{
            DataResponseIdService idService = dataResponseIdFactory.getIdService(key);
            if (dataMappingEntryType == null || DefaultDataMappingEntry.class == dataMappingEntryType) {
                return new DefaultDataMappingEntry(key, idService);
            } else {
                return DataMappingEntry.create(dataMappingEntryType).init(key, idService);
            }
        });
    }

    public void put(String key, DataResponseEntry dataResponseEntry) {
        DataMappingEntry dataMappingEntry = getDataMappingEntry(key);
        dataMappingEntry.add(dataResponseEntry, timeout);
    }


    private void init() {
        this.clearLoopThread.scheduleWithFixedDelay(this::clearLoopRun, 10, 10, TimeUnit.SECONDS);
    }

    /**
     * 通过异步进行清理
     */
    private void clearLoopRun() {
        try {
            Collection<DataMappingEntry> values = dataEntryMap.values();
            List<Future<?>> futures = new ArrayList<>(dataEntryMap.size());
            for (DataMappingEntry value : values) {
                Future<?> future = clearThread.submit(value::clearInvalid);
                futures.add(future);
            }
            for (Future<?> future : futures) {
                try {
                    future.get();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void config(ServerConfig serverConfig) {
        // 配置数据队列的映射实体, 用于存储队列的数据
        String dataQueueEntryClassname = serverConfig.getDataQueueEntryClassname();
        this.setDataMappingEntryType((Class<? extends DataMappingEntry>) BeanUtil.getClassByName(dataQueueEntryClassname));
        // 配置数据的数据ID生成算法
        String dataIdFactoryClassname = serverConfig.getDataIdFactoryClassname();
        this.setDataResponseIdFactory((DataResponseIdFactory) BeanUtil.newInstance(dataIdFactoryClassname));
        // 数据队列过期清理线程数
        int dataQueueClearThreadSize = serverConfig.getDataQueueClearThreadSize();
        clearThread = Executors.newFixedThreadPool(dataQueueClearThreadSize, new ThreanName("dataQueue-ClearThread", true));
        // 初始化数据复用队列
        this.init();
    }
}
